/**
 * Copyright &copy; 2021-2026 <a href="http://www.jeeplus.org/">JeePlus</a> All rights reserved.
 */
package com.jeeplus.flowable.service;

import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.node.ObjectNode;
import com.google.common.collect.Lists;
import com.jeeplus.flowable.constant.FlowableConstant;
import com.jeeplus.flowable.model.TaskComment;
import com.jeeplus.flowable.service.converter.json.FlowModelService;
import com.jeeplus.flowable.vo.ActionType;
import com.jeeplus.flowable.vo.ProcessStatus;
import com.jeeplus.flowable.vo.ProcessVo;
import com.jeeplus.flowable.vo.TaskVo;
import com.jeeplus.sys.service.dto.UserDTO;
import com.jeeplus.sys.utils.UserUtils;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.io.FileUtils;
import org.apache.commons.io.FilenameUtils;
import org.flowable.bpmn.constants.BpmnXMLConstants;
import org.flowable.bpmn.converter.BpmnXMLConverter;
import org.flowable.bpmn.model.BpmnModel;
import org.flowable.bpmn.model.EndEvent;
import org.flowable.bpmn.model.FlowNode;
import org.flowable.bpmn.model.Process;
import org.flowable.editor.constants.ModelDataJsonConstants;
import org.flowable.editor.language.json.converter.BpmnJsonConverter;
import org.flowable.engine.HistoryService;
import org.flowable.engine.RepositoryService;
import org.flowable.engine.RuntimeService;
import org.flowable.engine.TaskService;
import org.flowable.engine.history.HistoricActivityInstance;
import org.flowable.engine.history.HistoricProcessInstance;
import org.flowable.engine.history.HistoricProcessInstanceQuery;
import org.flowable.engine.repository.Deployment;
import org.flowable.engine.repository.Model;
import org.flowable.engine.repository.ProcessDefinition;
import org.flowable.engine.repository.ProcessDefinitionQuery;
import org.flowable.engine.runtime.Execution;
import org.flowable.engine.runtime.ProcessInstance;
import org.flowable.engine.runtime.ProcessInstanceQuery;
import org.flowable.identitylink.api.IdentityLink;
import org.flowable.task.api.Task;
import org.flowable.ui.common.security.SecurityUtils;
import org.flowable.ui.modeler.serviceapi.ModelService;
import org.flowable.variable.api.history.HistoricVariableInstance;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.multipart.MultipartFile;

import javax.xml.stream.XMLInputFactory;
import javax.xml.stream.XMLStreamException;
import javax.xml.stream.XMLStreamReader;
import java.io.*;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
import java.util.zip.ZipInputStream;

/**
 * 流程定义相关Service
 *
 * @author jeeplus
 * @version 2021-09-03
 */
@Slf4j
@Service
@Transactional
public class FlowProcessService {

    @Autowired
    private RepositoryService repositoryService;
    @Autowired
    private ModelService modelService;
    @Autowired
    private RuntimeService runtimeService;
    @Autowired
    private HistoryService historyService;
    @Autowired
    private FlowModelService flowModelService;
    @Autowired
    private TaskService taskService;
    @Autowired
    private FlowableBpmnModelService flowableBpmnModelService;

    /**
     * 根据key获取流程
     */
    public ProcessDefinition getProcessDefinitionByKey(String key) {

        ProcessDefinitionQuery processDefinitionQuery = repositoryService.createProcessDefinitionQuery ( )
                .latestVersion ( ).orderByProcessDefinitionKey ( ).asc ( );
        processDefinitionQuery.processDefinitionKey ( key );
        List <ProcessDefinition> processDefinitionList = processDefinitionQuery.list ( );
        if ( processDefinitionList.size ( ) > 0 ) {
            return processDefinitionList.get ( 0 );
        } else {
            return null;
        }
    }

    public boolean isAuth(UserDTO user, String processDefId) {
        List <IdentityLink> identityLinks = repositoryService.getIdentityLinksForProcessDefinition ( processDefId );
        if ( identityLinks.size ( ) == 0 ) {
            return true;
        }
        for (IdentityLink identityLink : identityLinks) {
            if ( user.getId ( ).equals ( identityLink.getUserId ( ) ) ) {
                return true;
            }
            if ( ("," + user.getRoleIds ( ) + ",").contains ( "," + identityLink.getGroupId ( ) + "," ) ) {
                return true;
            }
        }
        return false;
    }

    /**
     * 流程定义列表
     */
    public Page <Map> processList(Page <Map> page, String category) {
        ProcessDefinitionQuery processDefinitionQuery = repositoryService.createProcessDefinitionQuery ( ).active ( )
                .latestVersion ( ).orderByProcessDefinitionKey ( ).asc ( );
        if ( StrUtil.isNotEmpty ( category ) ) {
            processDefinitionQuery.processDefinitionCategory ( category );
        }

        List <ProcessDefinition> processDefinitionList = processDefinitionQuery.list ( );
        List records = Lists.newArrayList ( );
        for (ProcessDefinition processDefinition : processDefinitionList) {
            if ( this.isAuth ( UserUtils.getCurrentUserDTO ( ), processDefinition.getId ( ) ) ) {
                String deploymentId = processDefinition.getDeploymentId ( );
                Deployment deployment = repositoryService.createDeploymentQuery ( ).deploymentId ( deploymentId ).singleResult ( );
                Map pMap = new HashMap <> ( );
                pMap.put ( "id", processDefinition.getId ( ) );
                pMap.put ( "category", processDefinition.getCategory ( ) );
                pMap.put ( "key", processDefinition.getKey ( ) );
                pMap.put ( "name", processDefinition.getName ( ) );
                pMap.put ( "version", "V:" + processDefinition.getVersion ( ) );
                pMap.put ( "resourceName", processDefinition.getResourceName ( ) );
                pMap.put ( "diagramResourceName", processDefinition.getDiagramResourceName ( ) );
                pMap.put ( "deploymentId", processDefinition.getDeploymentId ( ) );
                pMap.put ( "suspended", processDefinition.isSuspended ( ) );
                pMap.put ( "deploymentTime", deployment.getDeploymentTime ( ) );
                records.add ( pMap );
            }
        }
        page.setRecords ( records );
        page.setTotal ( page.getRecords ( ).size ( ) );
        return page;
    }

    /**
     * 运行中的流程
     */
    public Page <ProcessVo> runningList(Page <ProcessInstance> page, String title, String procInsId, String procDefKey) throws Exception {
        List <ProcessVo> result = new ArrayList <ProcessVo> ( );
        Page <ProcessVo> resultPage = new Page <> ( );
        ProcessInstanceQuery processInstanceQuery = runtimeService.createProcessInstanceQuery ( ).includeProcessVariables ( );

        if ( StrUtil.isNotBlank ( procInsId ) ) {
            processInstanceQuery.processInstanceId ( procInsId );
        }

        if ( StrUtil.isNotBlank ( procDefKey ) ) {
            processInstanceQuery.processDefinitionKey ( procDefKey );
        }

        if ( StrUtil.isNotBlank ( title ) ) {
            processInstanceQuery.variableValueLike ( "title", "%" + title + "%" );
        }

        page.setTotal ( processInstanceQuery.count ( ) );
        resultPage.setTotal ( processInstanceQuery.count ( ) );

        List <ProcessInstance> runningList;
        if ( page.getSize ( ) == -1 ) {//不分页
            runningList = processInstanceQuery.list ( );
        } else {
            int start = (int) ((page.getCurrent ( ) - 1) * page.getSize ( ));
            int size = (int) (page.getSize ( ));
            runningList = processInstanceQuery.listPage ( start, size );
        }

        for (ProcessInstance p : runningList) {
            ProcessVo processVo = queryProcessState ( p.getProcessDefinitionId ( ), p.getId ( ) );
            processVo.setProcessInstanceId ( p.getProcessInstanceId ( ) );
            processVo.setProcessDefinitionId ( p.getProcessDefinitionId ( ) );
            processVo.setProcessDefinitionName ( p.getProcessDefinitionName ( ) );
            processVo.setActivityId ( p.getActivityId ( ) );
            processVo.setVars ( p.getProcessVariables ( ) );
            result.add ( processVo );
        }
        resultPage.setRecords ( result );
        return resultPage;
    }


    /**
     * 已结束的流程
     */
    public Page <ProcessVo> historyList(Page <ProcessVo> page, String title, String procInsId, String procDefKey) throws Exception {

        HistoricProcessInstanceQuery query = historyService.createHistoricProcessInstanceQuery ( ).includeProcessVariables ( ).finished ( )
                .orderByProcessInstanceEndTime ( ).desc ( );

        if ( StrUtil.isNotBlank ( procInsId ) ) {
            query.processInstanceId ( procInsId );
        }
        if ( StrUtil.isNotBlank ( title ) ) {
            query.variableValueLike ( "title", "%" + title + "%" );
        }

        if ( StrUtil.isNotBlank ( procDefKey ) ) {
            query.processDefinitionKey ( procDefKey );
        }

        page.setTotal ( query.count ( ) );

        List <HistoricProcessInstance> list;
        if ( page.getSize ( ) == -1 ) {
            list = query.list ( );
        } else {
            int start = (int) ((page.getCurrent ( ) - 1) * page.getSize ( ));
            int size = (int) (page.getSize ( ));
            list = query.listPage ( start, size );
        }
        List <ProcessVo> mapList = Lists.newArrayList ( );
        for (HistoricProcessInstance instance : list) {
            ProcessVo processVo = queryProcessState ( instance.getProcessDefinitionId ( ), instance.getId ( ) );
            processVo.setVars ( instance.getProcessVariables ( ) );
            processVo.setProcessDefinitionName ( instance.getProcessDefinitionName ( ) );
            processVo.setStartTime ( instance.getStartTime ( ) );
            processVo.setEndTime ( instance.getEndTime ( ) );
            processVo.setProcessInstanceId ( instance.getId ( ) );
            processVo.setProcessDefinitionId ( instance.getProcessDefinitionId ( ) );
            processVo.setDeleteReason ( instance.getDeleteReason ( ) );
            mapList.add ( processVo );
        }
        page.setRecords ( mapList );
        return page;
    }

    /**
     * 读取资源，通过部署ID
     */
    public InputStream resourceRead(String procDefId, String proInsId, String resType) throws Exception {

        if ( StrUtil.isBlank ( procDefId ) ) {
            ProcessInstance processInstance = runtimeService.createProcessInstanceQuery ( ).processInstanceId ( proInsId ).singleResult ( );
            procDefId = processInstance.getProcessDefinitionId ( );
        }
        ProcessDefinition processDefinition = repositoryService.createProcessDefinitionQuery ( ).processDefinitionId ( procDefId ).singleResult ( );

        String resourceName = "";
        if ( resType.equals ( "image" ) ) {
            resourceName = processDefinition.getDiagramResourceName ( );
        } else if ( resType.equals ( "xml" ) ) {
            resourceName = processDefinition.getResourceName ( );
        }

        InputStream resourceAsStream = repositoryService.getResourceAsStream ( processDefinition.getDeploymentId ( ), resourceName );
        return resourceAsStream;
    }

    /**
     * 部署流程 - 保存
     *
     * @param file
     * @return
     */
    public ResponseEntity deploy(String exportDir, String category, MultipartFile file) {

        String message = "";

        String fileName = file.getOriginalFilename ( );

        try {
            InputStream fileInputStream = file.getInputStream ( );
            Deployment deployment = null;
            String extension = FilenameUtils.getExtension ( fileName );
            if ( extension.equals ( "zip" ) || extension.equals ( "bar" ) ) {
                ZipInputStream zip = new ZipInputStream ( fileInputStream );
                deployment = repositoryService.createDeployment ( ).addZipInputStream ( zip ).deploy ( );
            } else if ( extension.equals ( "png" ) ) {
                deployment = repositoryService.createDeployment ( ).addInputStream ( fileName, fileInputStream ).deploy ( );
            } else if ( fileName.indexOf ( "bpmn20.xml" ) != -1 ) {
                deployment = repositoryService.createDeployment ( ).addInputStream ( fileName, fileInputStream ).deploy ( );
            } else if ( extension.equals ( "bpmn" ) ) { // bpmn扩展名特殊处理，转换为bpmn20.xml
                String baseName = FilenameUtils.getBaseName ( fileName );
                deployment = repositoryService.createDeployment ( ).addInputStream ( baseName + ".bpmn20.xml", fileInputStream ).deploy ( );
            } else {
                return ResponseEntity.badRequest ( ).body ( "不支持的文件类型：" + extension );
            }

            List <ProcessDefinition> list = repositoryService.createProcessDefinitionQuery ( ).deploymentId ( deployment.getId ( ) ).list ( );

            // 设置流程分类
            for (ProcessDefinition processDefinition : list) {
//					ActUtils.exportDiagramToFile(repositoryService, processDefinition, exportDir);
                repositoryService.setProcessDefinitionCategory ( processDefinition.getId ( ), category );
                message += "部署成功，流程ID=" + processDefinition.getId ( ) + "<br/>";
            }

            if ( list.size ( ) == 0 ) {
                return ResponseEntity.badRequest ( ).body ( "部署失败，没有流程。" );
            }

        } catch (Exception e) {
            e.printStackTrace ( );
            return ResponseEntity.badRequest ( ).body ( "部署失败:" + e.getMessage ( ) );
        }
        return ResponseEntity.ok ( message );
    }

    /**
     * 设置流程分类
     */
    public void updateCategory(String procDefId, String category) {
        repositoryService.setProcessDefinitionCategory ( procDefId, category );
    }

    /**
     * 挂起、激活流程实例
     */
    public String updateState(String state, String procDefId) {
        if ( state.equals ( "active" ) ) {
            repositoryService.activateProcessDefinitionById ( procDefId, true, null );
            return "已激活ID为[" + procDefId + "]的流程定义。";
        } else if ( state.equals ( "suspend" ) ) {
            repositoryService.suspendProcessDefinitionById ( procDefId, true, null );
            return "已挂起ID为[" + procDefId + "]的流程定义。";
        }
        return "无操作";
    }

    /**
     * 将部署的流程转换为模型
     *
     * @param procDefId
     * @throws UnsupportedEncodingException
     * @throws XMLStreamException
     */
    public Model convertToModel(String procDefId) throws UnsupportedEncodingException, XMLStreamException {
        ProcessDefinition processDefinition = repositoryService.createProcessDefinitionQuery ( ).processDefinitionId ( procDefId ).singleResult ( );
        InputStream bpmnStream = repositoryService.getResourceAsStream ( processDefinition.getDeploymentId ( ),
                processDefinition.getResourceName ( ) );
        XMLInputFactory xif = XMLInputFactory.newInstance ( );
        InputStreamReader in = new InputStreamReader ( bpmnStream, "UTF-8" );
        XMLStreamReader xtr = xif.createXMLStreamReader ( in );
        BpmnModel bpmnModel = new BpmnXMLConverter ( ).convertToBpmnModel ( xtr );

        BpmnJsonConverter converter = new BpmnJsonConverter ( );
        ObjectNode modelNode = converter.convertToJson ( bpmnModel );
        Model modelData = repositoryService.newModel ( );
        modelData.setKey ( processDefinition.getKey ( ) );
        modelData.setName ( processDefinition.getResourceName ( ) );
        modelData.setCategory ( processDefinition.getCategory ( ) );//.getDeploymentId());
        modelData.setDeploymentId ( processDefinition.getDeploymentId ( ) );
        modelData.setVersion ( Integer.parseInt ( String.valueOf ( repositoryService.createModelQuery ( ).modelKey ( modelData.getKey ( ) ).count ( ) + 1 ) ) );

        ObjectNode modelObjectNode = new ObjectMapper ( ).createObjectNode ( );
        modelObjectNode.put ( org.flowable.editor.constants.ModelDataJsonConstants.MODEL_NAME, processDefinition.getName ( ) );
        modelObjectNode.put ( org.flowable.editor.constants.ModelDataJsonConstants.MODEL_REVISION, modelData.getVersion ( ) );
        modelObjectNode.put ( ModelDataJsonConstants.MODEL_DESCRIPTION, processDefinition.getDescription ( ) );
        modelData.setMetaInfo ( modelObjectNode.toString ( ) );

        this.modelService.saveModel ( procDefId, modelData.getName ( ), modelData.getKey ( ), processDefinition.getDescription ( ), modelObjectNode.toString ( ), true, modelData.getVersion ( ).toString ( ), SecurityUtils.getCurrentUserId ( ) );

        return modelData;
    }

    /**
     * 导出图片文件到硬盘
     */
    public List <String> exportDiagrams(String exportDir) throws IOException {
        List <String> files = new ArrayList <String> ( );
        List <ProcessDefinition> list = repositoryService.createProcessDefinitionQuery ( ).list ( );

        for (ProcessDefinition processDefinition : list) {
            String diagramResourceName = processDefinition.getDiagramResourceName ( );
            String key = processDefinition.getKey ( );
            int version = processDefinition.getVersion ( );
            String diagramPath = "";

            InputStream resourceAsStream = repositoryService.getResourceAsStream (
                    processDefinition.getDeploymentId ( ), diagramResourceName );
            byte[] b = new byte[resourceAsStream.available ( )];

            @SuppressWarnings("unused")
            int len = -1;
            resourceAsStream.read ( b, 0, b.length );

            // create file if not exist
            String diagramDir = exportDir + "/" + key + "/" + version;
            File diagramDirFile = new File ( diagramDir );
            if ( !diagramDirFile.exists ( ) ) {
                diagramDirFile.mkdirs ( );
            }
            diagramPath = diagramDir + "/" + diagramResourceName;
            File file = new File ( diagramPath );

            // 文件存在退出
            if ( file.exists ( ) ) {
                // 文件大小相同时直接返回否则重新创建文件(可能损坏)
                log.debug ( "diagram exist, ignore... : {}", diagramPath );

                files.add ( diagramPath );
            } else {
                file.createNewFile ( );
                log.debug ( "export diagram to : {}", diagramPath );

                // wirte bytes to file
                FileUtils.writeByteArrayToFile ( file, b, true );

                files.add ( diagramPath );
            }

        }

        return files;
    }

    /**
     * 删除部署的流程，级联删除流程实例
     *
     * @param deploymentId 流程部署ID
     */
    public void deleteDeployment(String deploymentId) {
        repositoryService.deleteDeployment ( deploymentId, true );
    }

    /**
     * 删除运行中的流程实例
     *
     * @param procInsId    流程实例ID
     * @param deleteReason 删除原因，可为空
     */
    public void deleteProcIns(String procInsId, String deleteReason) {
        this.stopProcessInstanceById ( procInsId, ProcessStatus.DELETED, deleteReason );
    }


    /**
     * 终止流程实例
     *
     * @param procInsId     流程实例ID
     * @param processStatus 流程状态
     */
    public void stopProcessInstanceById(String procInsId, ProcessStatus processStatus, String comment) {
        ProcessInstance processInstance = runtimeService.createProcessInstanceQuery ( ).processInstanceId ( procInsId ).singleResult ( );
        if ( processInstance != null ) {
            Task currentTask = taskService.createTaskQuery ( ).processInstanceId ( procInsId ).list ( ).get ( 0 );
            //1、添加审批记录
            TaskComment taskComment = new TaskComment ( );
            if ( processStatus == ProcessStatus.REVOKE ) {
                taskComment.setType ( ActionType.REVOKE.getType ( ) );
                taskComment.setStatus ( ActionType.REVOKE.getStatus ( ) );
                taskComment.setMessage ( comment );
            } else if ( processStatus == ProcessStatus.STOP ) {
                taskComment.setType ( ActionType.STOP.getType ( ) );
                taskComment.setStatus ( ActionType.STOP.getStatus ( ) );
                taskComment.setMessage ( comment );
            } else if ( processStatus == ProcessStatus.REJECT ) {
                taskComment.setType ( ActionType.REJECT.getType ( ) );
                taskComment.setStatus ( ActionType.REJECT.getStatus ( ) );
                taskComment.setMessage ( comment );
            } else if ( processStatus == ProcessStatus.DELETED ) {
                taskComment.setType ( ActionType.DELETED.getType ( ) );
                taskComment.setStatus ( ActionType.DELETED.getStatus ( ) );
                taskComment.setMessage ( comment );
            }


            taskService.addComment ( currentTask.getId ( ), procInsId, taskComment.getCommentType ( ), taskComment.getFullMessage ( ) );
            if ( StrUtil.isBlank ( currentTask.getAssignee ( ) ) ) { // 未签收任务
                taskService.claim ( currentTask.getId ( ), UserUtils.getCurrentUserDTO ( ).getId ( ) );
            }
            runtimeService.setVariable ( procInsId, FlowableConstant.PROCESS_STATUS_CODE, processStatus.getCode ( ) );
            List <EndEvent> endNodes = flowableBpmnModelService.findEndFlowElement ( processInstance.getProcessDefinitionId ( ) );
            String endId = endNodes.get ( 0 ).getId ( );
            //2、执行终止
            List <Execution> executions = runtimeService.createExecutionQuery ( ).parentId ( procInsId ).list ( );
            List <String> executionIds = new ArrayList <> ( );
            executions.forEach ( execution -> executionIds.add ( execution.getId ( ) ) );
            this.moveExecutionsToSingleActivityId ( executionIds, endId );


        }
    }

    /**
     * 撤销流程实例
     *
     * @param procInsId 流程实例ID
     */
    public void callBackProcessInstanceById(String procInsId) {

        this.stopProcessInstanceById ( procInsId, ProcessStatus.REVOKE, ProcessStatus.REVOKE.getStatus ( ) );


    }


    /**
     * 执行跳转
     */
    protected void moveExecutionsToSingleActivityId(List <String> executionIds, String activityId) {
        runtimeService.createChangeActivityStateBuilder ( )
                .moveExecutionsToSingleActivityId ( executionIds, activityId )
                .changeState ( );
    }

    /**
     * 删除历史流程
     */
    public void delHistoryProcInsById(String procInsId) {
        historyService.deleteHistoricProcessInstance ( procInsId );
    }

    /**
     * 获取流程定义
     */
    public ProcessDefinition getProcessDefinition(String proDefId) {
        ProcessDefinitionQuery processDefinitionQuery = repositoryService.createProcessDefinitionQuery ( )
                .latestVersion ( ).orderByProcessDefinitionKey ( ).asc ( );
        if ( StrUtil.isNotBlank ( proDefId ) ) {
            processDefinitionQuery.processDefinitionId ( proDefId );
        }
        ProcessDefinition p = processDefinitionQuery.singleResult ( );
        return p;
    }


    /**
     * 读取流程文件
     */
    public Map getImageStream(String processDefId) {
        Map m = new HashMap ( );
        try {
            BpmnModel bpmnModel = repositoryService.getBpmnModel ( processDefId );
            byte[] bpmnBytes = flowModelService.getBpmnXML ( bpmnModel );
            m.put ( "bpmnXml", new String ( bpmnBytes ) );
            return m;
        } catch (Exception e) {
            e.printStackTrace ( );
        }
        return null;
    }


    /**
     * 查询流程实例状态
     *
     * @return
     * @throws Exception
     */
    public ProcessVo queryProcessState(String processDefinitionId, String processInstanceId) throws Exception {
        ProcessVo processVo = new ProcessVo ( );
        // 通过流程实例ID查询流程实例
        ProcessInstance pi = runtimeService.createProcessInstanceQuery ( )
                .processInstanceId ( processInstanceId ).singleResult ( );
        if ( pi != null ) {
            if ( pi.isSuspended ( ) ) {
                //挂起实例
                processVo.setProcessStatus ( ProcessStatus.SUSPENDED );
                return processVo;
            } else {
                //执行实例
                processVo.setProcessStatus ( ProcessStatus.WAITING );
                if ( taskService.createTaskQuery ( ).processInstanceId ( processInstanceId ).list ( ).size ( ) != 0 ) {
                    Task currentTask = taskService.createTaskQuery ( ).processInstanceId ( processInstanceId ).list ( ).get ( 0 );
                    processVo.setTask ( new TaskVo ( currentTask ) );
                    processVo.setTaskName ( currentTask.getName ( ) );
                }
                return processVo;
            }

        } else {
            HistoricProcessInstance pi2 = historyService.createHistoricProcessInstanceQuery ( ).processInstanceId ( processInstanceId ).singleResult ( );
//            String endActivityId = pi2.getEndActi vityId ();
            List <HistoricActivityInstance> historicActivityInstanceList = historyService.createHistoricActivityInstanceQuery ( ).processInstanceId ( processInstanceId ).finished ( ).orderByHistoricActivityInstanceEndTime ( ).desc ( ).list ( ).stream ( )
                    .filter ( activity -> activity.getActivityType ( ).equals ( BpmnXMLConstants.ELEMENT_TASK_USER )
                            || activity.getActivityType ( ).equals ( BpmnXMLConstants.ELEMENT_EVENT_END )
                            || activity.getActivityType ( ).equals ( BpmnXMLConstants.ELEMENT_EVENT_START ) ).distinct ( ).collect ( Collectors.toList ( ) );
            Process process = repositoryService.getBpmnModel ( processDefinitionId ).getMainProcess ( );
            String taskName = "";
            if ( historicActivityInstanceList.size ( ) != 0 ) {
                HistoricActivityInstance historicActivityInstance = historicActivityInstanceList.get ( 0 );
                FlowNode flowElement = (FlowNode) process.getFlowElement ( historicActivityInstance.getActivityId ( ), true );
                taskName = flowElement.getName ( );
            }

            if ( pi2 != null ) {

                HistoricVariableInstance processStatusInstance = historyService.createHistoricVariableInstanceQuery ( ).processInstanceId ( processInstanceId ).variableName ( FlowableConstant.PROCESS_STATUS_CODE ).singleResult ( );

                if ( pi2.getDeleteReason ( ) == null && processStatusInstance == null ) {
                    processVo.setProcessStatus ( ProcessStatus.AGREE );
                    processVo.setTaskName ( taskName );
                    return processVo;
                } else if ( pi2.getDeleteReason ( ) != null ) {
                    processVo.setProcessStatus ( ProcessStatus.STOP );
                    processVo.setTaskName ( taskName );
                    processVo.setDeleteReason ( pi2.getDeleteReason ( ) );
                    return processVo;
                } else {
                    int code = Integer.valueOf ( processStatusInstance.getValue ( ).toString ( ) );
                    ProcessStatus processStatus = ProcessStatus.value ( code );
                    processVo.setProcessStatus ( processStatus );
                    processVo.setTaskName ( taskName );
                    return processVo;
                }

            } else {
                processVo.setProcessStatus ( ProcessStatus.DELETED );
                processVo.setTaskName ( taskName );
                return processVo;
            }

        }
    }

}
